package com.weasel.weixin.commons.session.mgt;

import com.weasel.weixin.commons.exception.ExpiredSessionException;
import com.weasel.weixin.commons.exception.InvalidSessionException;
import com.weasel.weixin.commons.session.WeixinSession;
import com.weasel.weixin.commons.session.WeixinValidatingSession;
import com.weasel.weixin.commons.session.mgt.scheduler.ExecutorServiceSessionValidationScheduler;
import com.weasel.weixin.commons.session.mgt.scheduler.SessionValidationScheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by Dylan on 2015/12/7.
 */
public abstract class AbstractWeixinValidatingSessionManager extends AbstractWeixinSessionManager implements WeixinValidatingSessionManager{

    private final static Logger logger = LoggerFactory.getLogger(AbstractWeixinValidatingSessionManager.class);

    public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = MILLIS_PER_MINUTE;

    protected AtomicBoolean sessionValidationSchedulerEnabled;

    protected long sessionValidationInterval;

    protected SessionValidationScheduler sessionValidationScheduler;

    public AbstractWeixinValidatingSessionManager() {
        sessionValidationSchedulerEnabled = new AtomicBoolean(true);
        sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
    }

    public boolean isSessionValidationSchedulerEnabled() {
        return sessionValidationSchedulerEnabled.get();
    }

    public void setSessionValidationSchedulerEnabled(boolean _sessionValidationSchedulerEnabled) {
        this.sessionValidationSchedulerEnabled.set(_sessionValidationSchedulerEnabled);
    }

    public long getSessionValidationInterval() {
        return sessionValidationInterval;
    }

    public void setSessionValidationInterval(long sessionValidationInterval) {
        this.sessionValidationInterval = sessionValidationInterval;
    }

    public SessionValidationScheduler getSessionValidationScheduler() {
        return sessionValidationScheduler;
    }

    public void setSessionValidationScheduler(SessionValidationScheduler sessionValidationScheduler) {
        this.sessionValidationScheduler = sessionValidationScheduler;
    }

    private void enableSessionValidationIfNecessary(){
        SessionValidationScheduler sessionValidationScheduler = getSessionValidationScheduler();
        if(isSessionValidationSchedulerEnabled() && (null == sessionValidationScheduler || !sessionValidationScheduler.isEnabled())){
            enableSessionValidation();
        }
    }

    private void validate(WeixinSession session) throws InvalidSessionException {

        try {
            doValidate(session);
        }catch (InvalidSessionException ise){
            onInvalidation(session);
            throw ise;
        }
    }

    private void doValidate(WeixinSession session) throws InvalidSessionException{
        if(session instanceof WeixinValidatingSession){
            ((WeixinValidatingSession)session).validate();
        }else{
            String msg = "the sessin is not impletments "+WeixinValidatingSession.class.getName();
            throw new IllegalStateException(msg);
        }
    }

    private void onInvalidation(WeixinSession session){
        onStop(session);
    }

    protected void enableSessionValidation(){
        SessionValidationScheduler sessionValidationScheduler = getSessionValidationScheduler();

        if(null == sessionValidationScheduler){
            sessionValidationScheduler = createSessionValidationScheduler();
            setSessionValidationScheduler(sessionValidationScheduler);
        }

        if(logger.isInfoEnabled()){
            logger.info("enable session validation");
        }
        sessionValidationScheduler.enableSessionValidation();
    }

    private SessionValidationScheduler createSessionValidationScheduler(){
        if(logger.isDebugEnabled()){
            logger.debug("create a session validation scheduler");
        }

        ExecutorServiceSessionValidationScheduler sessionValidationScheduler = new ExecutorServiceSessionValidationScheduler(this);
        sessionValidationScheduler.setInterval(getSessionValidationInterval());
        return sessionValidationScheduler;
    }

    protected void disableSessionValidation(){
        if(logger.isInfoEnabled()){
            logger.info("disable session validation");
        }

        SessionValidationScheduler sessionValidationScheduler = getSessionValidationScheduler();
        if(null != sessionValidationScheduler){
            sessionValidationScheduler.disableSessionValidation();
            setSessionValidationScheduler(null);
        }
    }

    @Override
    public void validateSessions() {
        Collection<WeixinSession> sessions = getActiveSessions();
        if(logger.isInfoEnabled()){
            logger.info("start validate session");
        }

        int invalidCount = 0;

        if(null != sessions && !sessions.isEmpty()){
            for(WeixinSession session : sessions){
                if(logger.isDebugEnabled()){
                    String msg = "validate session with id [{}]";
                    logger.debug(msg,session.getId());
                }
                try {
                    validate(session);
                }catch (InvalidSessionException ise){

                    if (logger.isDebugEnabled()) {
                        boolean expired = (ise instanceof ExpiredSessionException);
                        String msg = "Invalidated session with id [" + session.getId() + "]" +
                                (expired ? " (expired)" : " (stopped)");
                        logger.debug(msg);
                    }
                    invalidCount++;
                }
            }
        }

        if (logger.isInfoEnabled()) {
            String msg = "Finished session validation.";
            if (invalidCount > 0) {
                msg += "  [" + invalidCount + "] sessions were stopped.";
            } else {
                msg += "  No sessions were stopped.";
            }
            logger.info(msg);
        }
    }

    @Override
    protected void doCreateSession(WeixinSession session) {
        enableSessionValidationIfNecessary();
        afterCreateSession(session);
    }

    protected abstract void afterCreateSession(WeixinSession session);

    protected abstract Collection<WeixinSession> getActiveSessions();


}
